#include "Timer.h"

#include <Windows.h>
#include <MMSystem.h>

using namespace Pantsu;

static bool   qpcFlag;
static double qpcFrequency;

unsigned systemTime() {

	if ( qpcFlag ) {
		static LONGLONG qpcMillisPerTick;
		QueryPerformanceCounter((LARGE_INTEGER*)&qpcMillisPerTick);
        return (unsigned)(qpcMillisPerTick * qpcFrequency);
	} else {
        return unsigned(timeGetTime());
    }
}


unsigned Timer::GetTime() {
    return systemTime();
}

unsigned long systemClock() {
    __asm {
        rdtsc;
    }
}

unsigned long Timer::GetClock() {
    return systemClock();
}

// Sets up the timing system and registers the performance timer.
void initTime() {
    LONGLONG time;

    qpcFlag = (QueryPerformanceFrequency((LARGE_INTEGER*)&time) > 0);

    // Check if we have access to the performance counter at this
    // resolution.
    if (qpcFlag) qpcFrequency = 1000.0 / time;
}


// Holds the global frame time that is passed around
static Timer *timingData = NULL;

// Retrieves the global frame info instance
Timer& Timer::get() {
    return (Timer&)*timingData;
}

// Updates the global frame information. Should be called once per frame.
void Timer::Update() {

    if (!timingData) return;

    // Advance the frame number.
	if (!timingData->m_bIsPaused)
    {
		timingData->m_uFrameNumber++;
    }

    // Update the timing information.
    unsigned thisTime = systemTime();
	timingData->m_uLastFrameDuration = thisTime - timingData->m_uLastFrameTimeStamp;
    timingData->m_uLastFrameTimeStamp = thisTime;

    // Update the tick information.
    unsigned long thisClock = systemClock();
	timingData->m_ulLastFrameClockTicks = thisClock - timingData->m_ulLastFrameClockStamp;
    timingData->m_ulLastFrameClockStamp = thisClock;

    // Update the RWA frame rate if we are able to.
    if (timingData->m_uFrameNumber > 1) {
		if (timingData->m_dAverageFrameDuration <= 0)
        {
            timingData->m_dAverageFrameDuration = (double)timingData->m_uLastFrameDuration;
        }
        else
        {
            // RWA over 100 frames.
            timingData->m_dAverageFrameDuration *= 0.99;
            timingData->m_dAverageFrameDuration += 0.01 * (double)timingData->m_uLastFrameDuration;

            // Invert to get FPS
			timingData->m_fFPS = (float)(1000.0/timingData->m_dAverageFrameDuration);
        }
    }
}

void Timer::Init() {
    // Set up the timing system.
    initTime();

    // Create the frame info object
    if (!timingData) timingData = new Timer();

    // Set up the frame info structure.
    timingData->m_uFrameNumber = 0;

    timingData->m_uLastFrameTimeStamp = systemTime();
    timingData->m_uLastFrameDuration = 0;

    timingData->m_ulLastFrameClockStamp = systemClock();
    timingData->m_ulLastFrameClockTicks = 0;

	timingData->m_bIsPaused = false;

    timingData->m_dAverageFrameDuration = 0;
	timingData->m_fFPS = 0;
}

void Timer::DeInit() {
        delete timingData;
        timingData = NULL;
}